package ar.unrn.tp1.persistence.db4o;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.TreeMap;

import org.joda.time.DateTime;

import ar.unrn.tp1.model.Articulo;
import ar.unrn.tp1.model.Autor;
import ar.unrn.tp1.model.Hemeroteca;
import ar.unrn.tp1.model.Revista;

import com.db4o.Db4oEmbedded;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.config.EmbeddedConfiguration;
import com.db4o.query.Predicate;

public class DB4OHemeroteca implements Hemeroteca {

	private String dbName;

	public DB4OHemeroteca() {
		// Inject db path and Name. Using Spring JVM args
		this.dbName = System.getProperty("user.home") + "/tp1.db4o";
	}

	@Override
	public void nuevaRevista(Revista revista) {
		ObjectContainer db = startSession();
		try {

			db.store(revista);

		} finally {
			closeSession(db);
		}
	}

	@SuppressWarnings("serial")
	@Override
	public List<Articulo> articulosPorRevista(final DateTime edicion) {
		ObjectContainer db = startSession();
		try {

			List<Revista> revista = db.query(new Predicate<Revista>() {
				public boolean match(Revista revista) {
					return revista.esEdicion(edicion);
				}
			});

			if (revista.size() > 0) {
				return new ArrayList<Articulo>(revista.get(0).getArticulos());
			}

			return new ArrayList<Articulo>();

		} finally {
			closeSession(db);
		}
	}

	@SuppressWarnings("serial")
	@Override
	public List<Autor> autoresPorArticulo(final String nombreArticulo) {
		ObjectContainer db = startSession();
		try {

			List<Articulo> articulos = db.query(new Predicate<Articulo>() {
				public boolean match(Articulo articulo) {
					return articulo.tieneTitulo(nombreArticulo);
				}
			});

			if (articulos.size() > 0) {
				return new ArrayList<Autor>(articulos.get(0).getAutores());
			}

			return new ArrayList<Autor>();

		} finally {
			closeSession(db);
		}
	}

	@SuppressWarnings("serial")
	@Override
	public List<Articulo> articulosPorTema(final String tema) {
		ObjectContainer db = startSession();
		try {

			List<Articulo> articulos = db.query(new Predicate<Articulo>() {
				public boolean match(Articulo articulo) {
					return articulo.hablaSobreElTema(tema);
				}
			});

			return new ArrayList<Articulo>(articulos);

		} finally {
			closeSession(db);
		}
	}

	@SuppressWarnings("serial")
	@Override
	public Revista revistaMasViejaPorTema(final String tema) {
		ObjectContainer db = startSession();
		try {

			List<Revista> revistas = db.query(new Predicate<Revista>() {
				public boolean match(Revista revista) {
					return revista.hablaSobreElTema(tema);
				}
			});

			return Collections.min(revistas);

		} finally {
			closeSession(db);
		}
	}

	@Override
	public List<Object[]> revistasConMasDe2Articulos() {
		ObjectContainer db = startSession();
		try {

			ObjectSet<Revista> todasLasRevistas = db.query(Revista.class);

			// building a TreeMap with a comparator to store the Revistas
			// ordered by their number of articles
			TreeMap<Revista, Integer> groupedResults = new TreeMap<Revista, Integer>(
					new Comparator<Revista>() {
						@Override
						public int compare(Revista o1, Revista o2) {
							if (o1.cantidadDeArticulos().equals(
									o2.cantidadDeArticulos())) {
								return 0;
							}
							if (o1.cantidadDeArticulos() > o2
									.cantidadDeArticulos()) {
								return -1;
							} else {
								return 1;
							}
						}
					});

			// Adding the Revistar ordered
			for (Revista revista : todasLasRevistas) {
				groupedResults.put(revista, revista.cantidadDeArticulos());
			}

			// Getting the first one will give me the one with higher number of
			// articles
			Revista revista = groupedResults.firstKey();

			// Just building the result...
			List<Object[]> result = new ArrayList<Object[]>();
			result.add(new Object[] { revista.getEdicion(),
					revista.cantidadDeArticulos() });

			return result;
		} finally {
			closeSession(db);
		}

	}

	@SuppressWarnings("serial")
	@Override
	public Revista recuperarRevista(final DateTime edicion) {
		ObjectContainer db = startSession();
		try {

			List<Revista> revista = db.query(new Predicate<Revista>() {
				public boolean match(Revista revista) {
					return revista.esEdicion(edicion);
				}
			});

			if (revista.size() > 0) {
				return revista.get(0);
			}

			return null;

		} finally {
			closeSession(db);
		}
	}

	private ObjectContainer startSession() {
		EmbeddedConfiguration config = Db4oEmbedded.newConfiguration();

		return Db4oEmbedded.openFile(config, dbName);
	}

	private void closeSession(ObjectContainer db) {
		db.close();
	}
}
